/*
 * txcontroller.cc
 *
 *  Created on: 2011-1-9
 *      Author: xuhui
 */

#include <click/config.h>
#include "txcontroller.hh"
#include <clicknet/wifi.h>
#include <click/packet_anno.hh>

CLICK_DECLS

TXController::TXController(){
	_state = IDLE;
	_logcwmin = 5;
	_logcwmax = 10;
	_logcw = _logcwmin;
	_max_tries = 8;
	_tries = 0;
	_p = 0;
	_drops = 0;
}

TXController::~TXController(){

}

void *
TXController::cast(const char *n){
	if (strcmp(n, "TXController") == 0)
		return (TXController *)this;
	else
		return Element::cast(n);
}

int
TXController::configure(Vector<String> &conf, ErrorHandler *errh){
	if (cp_va_kparse(conf, this, errh,
		   "LOGCWMIN", 0, cpByte, &_logcwmin,
		   "LOGCWMAX", 0, cpByte, &_logcwmax,
		   "TRIES" , 0, cpUnsigned, &_max_tries,
		   cpEnd) < 0)
		return -1;
	return 0;
}

void
TXController::push(int port, Packet *p){
	struct click_wifi_extra *ceha = WIFI_EXTRA_ANNO(p);

	if(ceha->magic != WIFI_EXTRA_MAGIC || !(ceha->flags & WIFI_EXTRA_TX))
		p->kill();

	if(ceha->flags & WIFI_EXTRA_TX_FAIL)
		_state = FAILED;
	else
		_state = SUCCESS;

	_tries++;
	p->kill();
}

Packet *
TXController::pull(int ){
	switch (_state){
		case TXING:return 0;
		case FAILED:{
			if(_tries<_max_tries){
				inc_cw();
				set_cw();
				set_state(TXING);
				return _p;
			}
			drop_buffer();
			_p = input(0).pull();
			if(!_p){
				reset_cw();
				set_cw();
				set_state(TXING);
				return _p;
			}
			set_state(IDLE);
			return 0;
		}break;
		case SUCCESS:
		case IDLE:{
			drop_buffer();
			_p = input(0).pull();
			if(!_p){
				reset_cw();
				set_cw();
				set_state(TXING);
				return _p;
			}
			set_state(IDLE);
			return 0;
		}break;
	}
}

void TXController::drop_buffer(){
	if(_p==0)
		return;
	_p->kill();
	_p = 0;
	_tries = 0;
	_drops++;
}

void
TXController::set_cw(){
	struct click_wifi_extra *ceh = WIFI_EXTRA_ANNO(_p);
	ceh->magic = WIFI_EXTRA_MAGIC;
	ceh->current_cw = _logcw;
}

CLICK_ENDDECLS
EXPORT_ELEMENT(TXController)
